<?php

namespace fize\think\exception;

use fize\think\exception\HttpResponseException as FizeHttpResponseException;
use think\Exception;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\facade\App;
use think\facade\Env;
use think\facade\Request as RequestFacade;
use think\Request;
use think\Response;
use Throwable;

/**
 * HTTP异常处理类
 */
class HttpHandle extends Handle
{

    protected $ignoreReport = [
        ValidateException::class,
    ];

    /**
     * 渲染
     * @param Request   $request 请求
     * @param Throwable $e       异常
     * @return Response 返回响应对象
     */
    public function render($request, Throwable $e): Response
    {
        if ($e instanceof FizeHttpResponseException) {
            return $e->getResponse();
        }
        return parent::render($request, $e);
    }

    public function render2($request, Throwable $e): Response
    {
        if ($e instanceof RuntimeException) {
            $result = [
                'errcode' => $e->getCode(),
                'errmsg'  => $e->getMessage(),
            ];
            return json($result);
        } elseif ($e instanceof HttpException) {
            $result = [
                'errcode' => $e->getCode(),
                'errmsg'  => $e->getMessage(),
            ];
            return json($result, $e->getStatusCode());
        } else {
            $result = [
                'errcode' => 500,
                'errmsg'  => '系统错误'
            ];
            if (Env::get('APP_DEBUG')) {
                $result = [
                    'errcode' => $e->getCode() ?: -1,
                    'errmsg'  => $e->getMessage(),
                    'data'    => [
//                        'trace' => $e->getTrace()  // @todo trace输出时，文件、非可视化变量的处理。
                    ]
                ];
            }
            return json($result, 500);
        }
    }

    /**
     * 报告
     * @param Throwable $exception 异常
     */
    public function report(Throwable $exception): void
    {
        $response = null;
        if ($exception instanceof FizeHttpResponseException) {
            $response = $exception->getResponse();
            if ($previous = $exception->getPrevious()) {
                $this->report($previous);
            }
        }
        if ($exception instanceof HttpResponseException) {
            $response = $exception->getResponse();
        }
        if ($response) {
            $httpcode = $response->getCode();
            if (in_array($httpcode, [200, 204, 302, 303])) {
                return;
            }
        }

        if ($exception instanceof HttpException && $exception->getStatusCode() == 404) {
            return;
        }

        if ($this->isIgnoreReport($exception)) {
            return;
        }
        $this->stack($exception);
        parent::report($exception);
    }

    /**
     * 记录堆栈消息
     * @param Throwable $exception 错误对象
     * @return string
     */
    protected function stack(Throwable $exception)
    {
        $errmsg = $exception->getMessage() . "\n";

        $extend_data = [];
        if ($exception instanceof Exception) {
            $extend_data = $exception->getData();
        }
        if (isset($extend_data['Database Status']['Error SQL'])) {
            $errmsg .= '[ sql ] ' . $extend_data['Database Status']['Error SQL'] . "\n";
        }

        $errmsg .= '[ file ] ' . $exception->getFile() . " ";
        $errmsg .= '[ line ] ' . $exception->getLine() . " ";
        $errmsg .= '[ code ] ' . $exception->getCode() . "\n";
        $errmsg .= '[ IP ] ' . request()->ip() . "\n";
        $errmsg .= '[ user-agent ] ' . request()->header('user-agent') . "\n";
        $errmsg .= "----------trace----------\n";
        foreach ($exception->getTrace() as $trace) {
            $file = $trace['file'] ?? '';
            $line = $trace['line'] ?? '';
            $class = $trace['class'] ?? '';
            $function = $trace['function'] ?? '';
            $function = $class . ($trace['type'] ?? '') . $function;
            $errmsg .= '[ file ] ' . $file . " ";
            $errmsg .= '[ line ] ' . $line . " ";
            $errmsg .= '[ function ] ' . $function . "\n";
        }
        $errmsg .= "----------trace----------\n";

        if ($extend_data) {
            $errmsg .= "----------data----------\n";
            $errmsg .= print_r($extend_data, true) . "\n";
            $errmsg .= "----------data----------\n";
        }

        $post = RequestFacade::post();
        if ($post) {
            $errmsg .= "----------post----------\n";
            $errmsg .= print_r($post, true) . "\n";
            $errmsg .= "----------post----------\n";
        }

        $fname = date('Ymd-His', time()) . '.log';
        $dir = App::getRootPath() . '/runtime/common/stack/';
        mkdir($dir, 0777, true);
        $path = $dir . $fname;
        file_put_contents($path, $errmsg);
        return $fname;
    }
}